package controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.text.DateFormat;
import java.util.Date;

import javax.sound.sampled.AudioFormat;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;


import network.TCPCaller;
import network.TCPServer;
import network.TCPService;
import network.UDPRecordAndDispatch;

import view.PopupEditInfo;
import view.View;
import view.items.PanelAbstract;

import model.AudioFormatWrapper;
import model.Call;
import model.ChatMessage;
import model.EnumCallStatus;
import model.EnumSignalType;
import model.ItemComboBox;
import model.Model;
import model.Signal;

public class Controller implements ActionListener, ChangeListener {

	private Model model;
	private View view;

	public Controller(Model model) {
		this.model = model;
		
		
		UDPRecordAndDispatch udprd = new UDPRecordAndDispatch(model);
		udprd.start();
		
		try {
			int listenTCP =  Integer.parseInt(model.getProperties().getProperty("tcp_port"));
			TCPServer tcpServer = new TCPServer(InetAddress.getLocalHost(), listenTCP,this);
			tcpServer.start();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
		
	}

	@Override
	public void actionPerformed(ActionEvent a) {
		String source = a.getActionCommand();

		if (source  == "change") {	
			PopupEditInfo test = new PopupEditInfo(this.view);
			test.setVisible(true);
		}
		
		
		if (source == "menuExit") {
			exitSoftware();
		}
		
		if (source == "newcall") {
			showCallDialog();
		}
		if (source == "buttonCall") {
			initCall();
		}
		
		if (source == "buttonSoundSetting"){
			showSoundSetting();
		}

		
		if(source == "buttonAccept"){
			Call call = getCallFromAction(a);
			call.setStatus(EnumCallStatus.ACTIV_ESTABLISHED);
			Signal signal = new Signal();
			call.setLocalUdpPort(this.model.getFifo().poll());
			signal.factorySignalCallAccept(call.getLocalUdpPort(), this.model.getProperties().getProperty("firstname"),this.model.getProperties().getProperty("lastname") );
			call.getTcpService().sendSignal(signal);
			call.initCanaux();
		}
		
		if(source == "buttonDeny"){
			Call call = getCallFromAction(a);
		this.model.removeCall(call);
		Signal signal = new Signal();
		signal.factorySinalDenyCall();
		call.getTcpService().sendSignal(signal);
		}
		
		if(source == "buttonCancel"){
			Call call = getCallFromAction(a);
			this.model.removeCall(call);
		}
		
		if(source == "buttonPause"){
			Call call = getCallFromAction(a);
			pauseOn(call);
			
		}
		
		if(source == "buttonEnd"){
			Call call = getCallFromAction(a);
			this.endCall(call, true);
			
		}
		
		if(source == "buttonResume"){
			Call call = getCallFromAction(a);
			pauseOff(call);
			};

			
		if(source == "buttonSendChat"){
			sendChatMsg();
		}
		
		if(source == "buttonMuteSpeaker"){
			Call call = this.model.getSelectedCall();
			call.switchMuteSpeaker();
		}
		
		if(source == "buttonMuteMicro"){
			Call call = this.model.getSelectedCall();
			call.switchMuteMicrophone();
		}
		
		if(source == "buttonClear"){
			Call call = getCallFromAction(a);
			this.model.getAllCall().remove(call);
		}
		
		this.view.refreshAll();
	}

	public void exitSoftware() {
		for (Call call : model.getAllCall()) {
			if(call.getStatus() == EnumCallStatus.ACTIV_ESTABLISHED || call.getStatus() == EnumCallStatus.PENDING){
				this.endCall(call, true);
			}
		}
		System.exit(0);
		
	}

	private void pauseOff(Call call) {
		//Changement du statut du call
		call.setStatus(EnumCallStatus.ACTIV_ESTABLISHED);
		//Envoi du signal de PAUSE_OFF
		Signal signal = new Signal();
		signal.factorySignalCallPauseOff();
		call.getTcpService().sendSignal(signal);

	}

	private void pauseOn(Call call) {
		//Changement du statut du call
		call.setStatus(EnumCallStatus.PENDING);
		//Envoi du signal de PAUSE_ON
		Signal signal = new Signal();
		signal.factorySignalCallPauseOn();
		call.getTcpService().sendSignal(signal);
		}
	
	

	public void endCall(Call call, Boolean isInitiator) {
		
		//changement du statut du call
		call.setStatus(EnumCallStatus.ENDED);

		//coupure des 2 canaux UDP
		call.getUdpReceiveAndRead().setRunning(false);
		//call.getUdpRecordAndSend().setRunning(false);
	
		//On libère le port UDP
		int udpFree = call.getLocalUdpPort();
		this.model.getFifo().add(udpFree);
		
		
		//envoi du message END_CALL au correspondant si on est celui qui demande l'endCall
		if(isInitiator){
		System.out.println("[Controller] envoi d'un signal CALL_END");
		Signal signal = new Signal();
		signal.factorySignalCallEnd();
		call.getTcpService().setRunning(false);
		call.getTcpService().sendSignal(signal);
		}

		}

	private void sendChatMsg() {
		//Creation du message
		ChatMessage chatMessage = new ChatMessage(this.model.getProperties().getProperty("lastname"),this.view.getViewBottom().getPanelBottomChat().getTextChat().getText(), new Date());
		//ajout à notre model
		Call call = this.model.getSelectedCall();
		call.getAllChatMessage().add(chatMessage);
		//envoi au correspondant
		//1)Construction d'un Signal de message
			Signal signal = new Signal();
			signal.factorySignalSendChatMsg(chatMessage);
		//2) envoi du signal
			TCPService tcpService = call.getTcpService();
			tcpService.sendSignal(signal);
			//re initialisation du textfield
			this.view.getViewBottom().getPanelBottomChat().getTextChat().setText("");
	}

	

	private Call getCallFromAction(ActionEvent a) {
		JButton button = (JButton) a.getSource();
		PanelAbstract panelAbstract = (PanelAbstract) button.getParent();
		Call call = panelAbstract.getCall();
		return call;
	}

	private void showSoundSetting() {
		this.model.switchValueShowSoundSetting();
		
	}

	private void showCallDialog() {
		this.model.switchValueShowDialer();
	}

	private void initCall() {
		//recup les infos du formulaire : ip et port
		String ip = this.view.getViewDialer().getIpField().getText();
		int portTCPenFace = Integer.parseInt(this.view.getViewDialer().getPortField().getText());
		//recup les infos du formulaire : format du son
		float sampleRate = Float.parseFloat(this.view.getViewSoundSetting().getInputSampleRate().getText());
		int sampleSizeInBits = Integer.parseInt(this.view.getViewSoundSetting().getInputSampleSize().getText());
		int channels = Integer.parseInt(this.view.getViewSoundSetting().getInputChannel().getText());
		ItemComboBox first = (ItemComboBox) this.view.getViewSoundSetting().getComboboxSigned().getSelectedItem();
		boolean signed = first.getValue();
		ItemComboBox second = (ItemComboBox) this.view.getViewSoundSetting().getComboboxSigned().getSelectedItem();
		boolean endian = second.getValue();
		
//		System.out.println("Initiation d'un call avec envoi des parametres suivants : ");
//		System.out.println("sampleRate = "+sampleRate);
//		System.out.println("sampleSize = "+sampleSizeInBits);
//		System.out.println("channels = "+channels);
//		System.out.println("Signed = "+signed);
//		System.out.println("Endian = "+endian);
		AudioFormatWrapper audioToBeUse = new AudioFormatWrapper(sampleRate, sampleSizeInBits, channels, signed, endian);
		
		// verif des arguments(ip et port)
		if (Outils.checkIp(ip) && Outils.checkPort(portTCPenFace) && audioToBeUse.isValid()) {
			
			InetAddress ipEnFace = Outils.getInetAddress(ip);

			this.model.switchValueShowDialer();
			this.view.refreshAll();
			
			TCPCaller tcpCaller = new TCPCaller(ipEnFace, portTCPenFace,this,audioToBeUse);
			SwingUtilities.invokeLater(tcpCaller);
			

			
		} else {
			JOptionPane.showMessageDialog(null,
					"Bad values, try again", "Information",
					JOptionPane.INFORMATION_MESSAGE);
		}
		
	}

	public Model getModel() {
		return model;
	}

	public View getView() {
		return view;
	}

	public void setView(View view) {
		this.view = view;
	}

	public void stateChanged(ChangeEvent arg) {
		
		JSlider i = (JSlider)(arg.getSource());
		int valeur = i.getValue();
		this.model.getSelectedCall().setVoiceActivationLevel(valeur);
		this.view.getViewBottom().getPanelBottomInfoCall().getSlider().refresh();
		this.view.getViewBottom().getPanelBottomInfoCall().getVolumeSlider().setText(String.valueOf(valeur));
	}

	public void updateInfo(String[] param) {
		this.model.getProperties().setProperty("lastname", param[1]);
		this.model.getProperties().setProperty("firstname", param[0]);
		
	}

}
